﻿using System;
using System.Collections.Generic;

namespace Microsoft.Popfly.GameCreator.GameEngine.Events
{
    public interface IPropertyEventChecker
    {
        void InitPreviousValue();
        void CheckForChange();
    }

    public class PropertyChangeEventArgs : PGCEventArgs
    {
        public PropertyHolder instance { get; private set; }
        public string propertyName { get; private set; }
        public Object value { get; private set; }

        public PropertyChangeEventArgs(PropertyHolder holder, string _propertyName, Object _value)
        {
            instance = holder;
            propertyName = _propertyName;
            value = _value;
        }
    }

    public class PropertyChangeEventChecker : IEventChecker, IPropertyEventChecker
    {
        private Game m_game;
        internal PropertyHolder PropertyHolder;
        private string m_propertyName;
        private string m_previousValue;
        private PropertyCondition m_condition;
        private BehaviorOwner m_sender;
        private BehaviorOwner m_owner;

        public void InitPreviousValue()
        {
            m_previousValue = PropertyHolder.GetValue(m_propertyName).ToString();
        }
        public PropertyChangeEventChecker(Game game, BehaviorOwner owner, PropertyHolder propertyHolder, string propertyName)
        {
            m_game = game;
            m_owner = owner;
            PropertyHolder = propertyHolder;
            m_propertyName = propertyName;
            m_condition = new PropertyCondition(propertyHolder, propertyName, PropertyCondition.ConditionType.NOTEQUALS);

            Type holderType = PropertyHolder.GetType();
            if (holderType == typeof(Game))
            {
                m_sender = null;
            }
            else if (holderType.IsSubclassOf(typeof(BehaviorOwner)))
            {
                m_sender = (BehaviorOwner)PropertyHolder;
            }
            else
            {
                throw new NotImplementedException(
                    Utils.ErrorMessagePrefix +
                    "PropertyChangeEvents don't know how to handle PropertyHolders of type '" +
                    holderType.ToString() +
                    "'.");
            }
        }

        private bool m_changed;
        public void CheckForChange()
        {
            if (PropertyHolder.GetType() == typeof(Actor))
            {
                if (((Actor)PropertyHolder).Removed)
                {
                    m_changed = false;
                    m_owner.RemoveBehavior(this);
                }
            }
            // TODO: Using CheckCondition for this is super slow.  However, we need to come up with a way
            // of determining the type of the property to do anything more intelligent
            // and faster and this can't really be done without knowing what values the property 
            // will be set to
            m_changed = m_condition.CheckCondition(m_previousValue);
        }

        #region IEventChecker Members

        public bool FireThisFrame(double elapsedTimeInSeconds, out BehaviorOwner sender, out PGCEventArgs args)
        {
            bool retVal = false;
            
            if (m_changed)
            {

                sender = m_sender;

                args = new PropertyChangeEventArgs(PropertyHolder, m_propertyName, PropertyHolder.GetValue(m_propertyName));
                if (sender == null)
                    sender = m_game.CurrentScene;
                retVal = true;
            }
            else
            {
                sender = null;
                args = null;
            }
            return retVal;
        }

        public bool IsOfEventType(string eventType)
        {
            return eventType == "PropertyChange";
        }

        #endregion
    }

    public class MultiActorPropertyChangeEventChecker : IEventChecker, IPropertyEventChecker
    {
        private Game m_game;
        private Scene m_scene;
        private string m_propertyName;
        private BehaviorOwner m_sender;
        private BehaviorOwner m_owner;
        private string m_actorName;

        private Dictionary<string, string> m_previousValues = new Dictionary<string, string>();

        public void InitPreviousValue()
        {
            m_previousValues.Clear();
            foreach (Actor actor in m_scene.GetActors(m_actorName))
            {
                m_previousValues.Add(actor.Name, actor.GetValue(m_propertyName).ToString());
            }
        }
        public MultiActorPropertyChangeEventChecker(Game game, BehaviorOwner owner, string actorName, string propertyName)
        {
            m_game = game;
            m_scene = owner.Scene;
            m_owner = owner;
            m_actorName = actorName;
            m_propertyName = propertyName;

            
        }

        private bool m_changed;
        public void CheckForChange()
        {
            foreach (KeyValuePair<string, string> instanceValue in m_previousValues)
            {
                Actor actor = m_scene.GetActor(instanceValue.Key);

                if (actor != null && !actor.Removed)
                {
                    PropertyCondition condition = new PropertyCondition(actor, m_propertyName, PropertyCondition.ConditionType.NOTEQUALS);
                    if (condition.CheckCondition(instanceValue.Value))
                    {
                        m_sender = actor;
                        m_changed = true;
                        return;
                    }
                }
            }
            m_changed = false;
        }

        #region IEventChecker Members

        public bool FireThisFrame(double elapsedTimeInSeconds, out BehaviorOwner sender, out PGCEventArgs args)
        {
            bool retVal = false;

            if (m_changed)
            {

                sender = m_sender;

                args = new PropertyChangeEventArgs(m_sender, m_propertyName, m_sender.GetValue(m_propertyName));
                
                retVal = true;
            }
            else
            {
                sender = null;
                args = null;
            }
            return retVal;
        }

        public bool IsOfEventType(string eventType)
        {
            return eventType == "PropertyChange";
        }

        #endregion
    }
}